home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / GraphicViewers / Movie / Source / MovieView.m < prev    next >
Text File  |  1993-12-12  |  22KB  |  856 lines

  1.  
  2. #include <objc/NXBundle.h>
  3. #include <dpsclient/psops.h>
  4. #include "wraps.h"
  5.  
  6. #import "MovieView.h"
  7.  
  8. /*
  9.  * Movie 2.51 - 5/7/92 pjf
  10.  *
  11.  * Differences between 2.5 and 2.51:
  12.  * - the save: method actually has a prayer of working when the
  13.  *   user saves on top of an existing movie and the old copy can't be renamed.
  14.  *
  15.  * Differences between 2.0 and 2.5:
  16.  * - buttons to control cache depth
  17.  *
  18.  * - turn off multiframe .tiffs by default (define BC_VERSION_1 if you
  19.  *   have a .tiff movie and are too lazy to use tiffutil to turn
  20.  *   it into an .anim directory)
  21.  *
  22.  * - now able to save movie (currently-selected depth)
  23.  *
  24.  */
  25.  
  26. #define maxFrames 1024
  27.  
  28. @implementation MovieView
  29.  
  30. void error(const char *format, ...)
  31.  {
  32.     va_list ap;
  33.     
  34.     va_start(ap, format);
  35.     vfprintf(stderr, format, ap);
  36.     va_end(ap);
  37.     kill(getpid(),6);
  38.  }
  39.  
  40. //
  41. - initFrame:(const NXRect *) frameRect
  42.  {
  43.     const char *x;
  44.     [(self = [super initFrame:frameRect]) allocateGState];
  45.     state = STOPPED;
  46.     mode = LOOP;
  47.     maxSize.width = maxSize.height = -1.0;
  48.     movieFrame = NULL;
  49.     frameCount = 0;
  50.     anim = nil;
  51.     pingDuringDisplay=NO;
  52.     x=NXGetDefaultValue("Movie","DefaultDepth");
  53.     if (!x) dmode=D_DEF; /* use default depth */
  54.     else switch(atoi(x)) {
  55.         default:
  56.         case 0: dmode=D_DEF; break;
  57.         case 2: dmode=D_2; break;
  58.         case 8: dmode=D_8; break;
  59.         case 12: dmode=D_12; break;
  60.         case 24: dmode=D_24; break;
  61.     };
  62.     updateControls = NO;
  63.     showFrameNumber = NO;
  64.     noOriginals = NO;
  65.     fromStream = NO;
  66.     return self;
  67.  }
  68.  
  69. - drawSelf:(const NXRect *) rects :(int)count
  70.  {
  71.     NXImage *image;
  72.     NXPoint origin = {0.0,0.0};
  73.     
  74.     if (!movieFrame) return nil;
  75.     image = movieFrame[currentFrameIndex].image;
  76.     if (!image) return self;
  77.     [image composite:NX_COPY toPoint:&origin];
  78.     if (!fromStream && (state==LOADING || showFrameNumber)) {
  79.     NXRect r;
  80.     [self getBounds:&r];
  81.     if (state==LOADING)
  82.         PSWtext(r.size.width, r.size.height, "Loading ...", currentFrameIndex+1);
  83.     else
  84.         PSWframe(r.size.width, r.size.height, currentFrameIndex+1);
  85.     }
  86.     if (pingDuringDisplay) NXPing();
  87.     if ((frameCount >= (int)frameRate)) {
  88.     if (state != STOPPED && state != LOADING) {
  89.         double t=[anim getDoubleRealTime]+[anim getSyncTime];
  90.         double afps=frameCount/(t-oldt);
  91.         [actualFpsText setDoubleValue:afps];
  92.         oldt=t;
  93.     }
  94.     frameCount=0;
  95.     }
  96.     if (updateControls) {
  97.     [fNumSlider setIntValue:currentFrameIndex];
  98.     [fNumText setIntValue:currentFrameIndex];
  99.     };
  100.     return self;
  101.  }
  102.  
  103.  
  104. //
  105. - (BOOL)open:sender
  106.  {
  107. #ifdef BC_VERSION_1
  108.   const char *const types[] = { "tiff", "anim", "mpg", (const char *) NULL };
  109. #else
  110.   const char *const types[] = { "anim", "mpg", (const char *) NULL };
  111. #endif
  112.   id pan = [OpenPanel new];
  113.   const char *const *filenames;
  114.   char filename[FILENAME_MAX];
  115.  
  116.   if (![pan runModalForTypes:types]) return NO;
  117.   if ((filenames = [pan filenames]) == NULL) return NO;
  118.   sprintf(filename,"%s/%s", [pan directory], filenames[0]);
  119.   strcpy(moviePath,filename);
  120.   return [self openFile:filename];
  121.  }
  122.  
  123. - (BOOL)play:sender
  124.  {
  125.      fromStream = YES;
  126.      noOriginals = YES;
  127.      return [self open:sender];
  128.  }
  129.  
  130. - makeWindow;
  131.  {
  132.     Window *w=[self window];
  133.     
  134.     [w sizeWindow:maxSize.width:maxSize.height]; /* will recache images */
  135.     [w setMiniwindowIcon:"movieDoc.tiff"];
  136.     [w makeKeyAndOrderFront:self];
  137.     [w display];
  138.     return self;
  139.  }
  140.  
  141. - makePanel:(char *)filename;
  142.  {
  143.     char ptitle[FILENAME_MAX];
  144.     char *ptr=rindex(filename,'/')+1;
  145.     
  146.     if (ptr == (char *)1) ptr=filename;
  147.     sprintf(ptitle,"Controls for %s",ptr);
  148.     [panel setTitle:ptitle];
  149.     [panel setNextResponder:[self window]];
  150.     [[fNumSlider setMaxValue:numFrames-1] setEnabled:YES];
  151.     [nFramesText setIntValue:numFrames-1];
  152.     [depthButtons selectCellAt:(int)dmode:0];
  153.     [panel orderFront:self];
  154.     return self;
  155.  }
  156.  
  157. - setFps:(float)fps;
  158.  {
  159.      frameRate = fps;
  160.     [fpsSlider setFloatValue:fps];
  161.     [fpsText setFloatValue:fps];
  162.     return self;
  163.  }
  164.  
  165. - setNoOriginals;
  166.  {
  167.     int i;
  168.     Window *w=[self window];
  169.  
  170.     if (noOriginals) return self;
  171.     if (movieFrame) for (i=0; i<numFrames; i++) if (movieFrame[i].original) {
  172.     [movieFrame[i].original free];
  173.     movieFrame[i].original = nil;
  174.     }
  175.     noOriginals = TRUE;
  176.     [w setMinSize:&maxSize];
  177.     [w setMaxSize:&maxSize];
  178.     return self;
  179.  }
  180.  
  181. // openFile: returns YES if the frames were successfully read, NO if not.
  182. - (BOOL)openFile:(char *)filename
  183.  {
  184.     char *ptr=rindex(filename,'.');
  185.  
  186.     if (!ptr) {
  187.     NXRunAlertPanel(NULL,"Impossible filename %s", NULL, NULL, NULL, filename);
  188.     return NO;
  189.     };
  190.  
  191.     state = LOADING;
  192.     [[self window] setTitleAsFilename:filename];
  193.  
  194.   /* get the bitmaps */
  195.     if (!strcmp(ptr,".anim")) {  /* the file is an Icon-style .anim directory */
  196.     char buf[FILENAME_MAX];
  197.     char *ptr2;
  198.     *ptr='\0'; /* clobber extension */
  199.     ptr2=1+rindex(filename,'/'); /* danger danger */
  200.     if (ptr2 == (char *)1) ptr2=filename; /* if not /full/path/name */
  201.     sprintf(buf,"%s.anim/%s",filename,ptr2);
  202.     [self openAnimDirectory:buf];
  203.     }
  204.     else if (!strcmp(ptr,".mpg")) { /* an MPEG file */
  205.     [self openMPEGfile:filename];
  206.     }
  207. #ifdef BC_VERSION_1
  208.     else if (!strcmp(ptr,".tiff")) { /* a slew o' TIFFs in one file */
  209.     bitmaps = [NXBitmapImageRep newListFromFile:filename];
  210.     if (!bitmaps) {
  211.         NXRunAlertPanel(NULL,"Couldn't get bitmaps from %s",
  212.         NULL,NULL,NULL, filename);
  213.         return NO;
  214.     };
  215.     [self allocateFrames:bitmaps];
  216.     [bitmaps free]; /* does not free elements */
  217.     [self setFps:15.0];
  218.     }
  219. #endif
  220.     else { /* this shouldn't happen */
  221.     NXRunAlertPanel(NULL,"Impossible filename %s",NULL,NULL,NULL,filename);
  222.     return NO;
  223.     };
  224.     state = STOPPED;
  225.     if (fromStream) [window close];
  226.     else [self makePanel:filename];
  227.     return YES;
  228.  }
  229.  
  230.  
  231. - (List *)listAnimDirectory:(char *)filenameRoot
  232.  {
  233.   List *bitmaps = [[List alloc] init];
  234.   int i=1;
  235.   while (1) {
  236.     char buf[FILENAME_MAX];
  237.     NXBitmapImageRep *newbitmap;
  238.     sprintf(buf,"%s.%d.tiff",filenameRoot,i++);
  239.     if ((access(buf,R_OK)) == -1) break;
  240.     newbitmap = [[NXBitmapImageRep alloc] initFromFile:buf];
  241.     if (!newbitmap) {
  242.       NXRunAlertPanel(NULL,"Couldn't get bitmap from %s",NULL,NULL,NULL,
  243.                     buf);
  244.       [[bitmaps freeObjects] free];
  245.       return nil;
  246.       }
  247.     else
  248.       [bitmaps addObject:newbitmap];
  249.     };
  250.   return bitmaps;
  251.  }
  252.  
  253. - openAnimDirectory:(char *)filenameRoot;
  254.  {
  255.     int i;
  256.     NXBitmapImageRep *newbitmap;
  257.     char buf[FILENAME_MAX];
  258.  
  259.     for (i=1; TRUE; i++) {
  260.     sprintf(buf,"%s.%d.tiff", filenameRoot, i);
  261.     if ((access(buf, R_OK)) == -1) break;
  262.     newbitmap = [[NXBitmapImageRep alloc] initFromFile:buf];
  263.     if (!newbitmap) {
  264.         NXRunAlertPanel(NULL,"Couldn't get bitmap from %s",NULL,NULL,NULL, buf);
  265.         continue;
  266.     }
  267.     [self addBitmap:newbitmap];
  268.     }
  269.     [self setFps:15.0];
  270.     return self;
  271.  }
  272.  
  273. - openMPEGfile:(char *)filename
  274.  {
  275.     int i, n, flen;
  276.     char command[256];
  277.     FILE *fd;
  278.     mpegInfo *pInfo;
  279.     long data;
  280.     BOOL swab;
  281.     NXStream *pStream;
  282.     NXBitmapImageRep *newbitmap;
  283.     
  284.     if (!(fd = fopen(filename, "r"))) error("Could not open %s", filename);
  285.     fread(&data, 4, 1, fd);
  286.     if (data!=0x000001b3 && data!=0xb3010000)
  287.     error("%s does not contain an mpeg stream: %x", filename, data);
  288.     if (data==0xb3010000) swab=YES; else swab=NO;
  289.     if (!(pInfo = calloc(1, sizeof(mpegInfo)))) error("Could not allocate pInfo.");
  290.     // Get horizontal and vertical size of image space
  291.     // as two 12 bit words, respectively
  292.     // then aspect ratio and picture rate
  293.     // as two 4 bit words.
  294.     fread(&data, 4, 1, fd); if (swab) data = NXSwapLong(data);
  295.     pInfo->picture_rate = 0x0F & data;
  296.     data >>= 4;
  297.     pInfo->aspect_ratio = 0x0F & data;
  298.     data >>= 4;
  299.     // In Motorola format, least significant bits come last
  300.     // v_size is actually the second value in the file
  301.     // i.e. h:12,v:12,a:4,p:4
  302.     pInfo->v_size = 0x0FFF & data;
  303.     pInfo->h_size = 0x0FFF & data >> 12;
  304.     maxSize.width = ((pInfo->h_size + 15) / 16) * 16.0;
  305.     maxSize.height = ((pInfo->v_size + 15) / 16) * 16.0;
  306.     // Get bit rate, vbv buffer size, and constrained parameter flag
  307.     fread(&data, 4, 1, fd); if (swab) data = NXSwapLong(data);
  308.     // throw away (non) intra quant matrix flags
  309.     data >>= 2;
  310.     pInfo->const_param_flag = 1 & data;
  311.     data >>= 1;
  312.     pInfo->vbv_buffer_size = 0x03FF & data;
  313.     data >>= 10 + 1;    // 1 marker bit
  314.     pInfo->bit_rate = 0x03FFFF & data;
  315.     fclose(fd);
  316.     pInfo->fps = pInfo->picture_rate;
  317.     flen = 3 * (int)maxSize.width * (int)maxSize.height;
  318.     
  319.  //    printf("Dimensions: %d %d, buffer size:%d\n", (int)maxSize.width, (int)maxSize.height, flen);
  320.     sprintf(command, "exec %s/mpegDecode %s",
  321.     [[NXBundle mainBundle] directory], filename);
  322.     if (!(fd = popen(command, "r")))
  323.     error("Could not create MPEG process:\n   %s", command);
  324.     pStream = NXOpenFile(fileno(fd), O_RDONLY);
  325.  
  326.  //    printf("Reading frames:\n");
  327.     for (i=0; TRUE; i++) {
  328.     newbitmap = [[NXBitmapImageRep alloc] initData:NULL
  329.         pixelsWide:(int)maxSize.width
  330.         pixelsHigh:(int)maxSize.height
  331.         bitsPerSample:8
  332.         samplesPerPixel:3    // (cSpace == RGB_COLOR) ? 3 : 1
  333.         hasAlpha:NO
  334.         isPlanar:NO
  335.         colorSpace:NX_RGBColorSpace
  336.         bytesPerRow:0
  337.         bitsPerPixel:0];
  338.     if (4!=NXRead(pStream, &data, 4)) {
  339.  //        printf("Finished.\n");
  340.         [newbitmap free];
  341.         break;
  342.     }
  343.  //    printf("Frame # %d.\n", data);
  344.     n = NXRead(pStream, [newbitmap data], flen);
  345.     if (n!=flen) error("Error reading image data (%d/%d bytes read).", n, flen);
  346.     [self addBitmap:newbitmap];
  347.     }
  348.     [self setFps:pInfo->fps];
  349.     return self;
  350.  }
  351.  
  352. - addBitmap:bm;
  353.  {
  354.     NXSize sz;
  355.     NXRect r;
  356.     int flen, flag=0;
  357.  
  358.     [bm getSize:&sz];
  359.     if (sz.width > maxSize.width) maxSize.width=sz.width, flag=1;
  360.     if (sz.height > maxSize.height) maxSize.height=sz.height, flag=1;
  361.     if (flag || !movieFrame) [[self window] sizeWindow:maxSize.width:maxSize.height];
  362.     flen = ([bm pixelsWide]*[bm pixelsHigh]*[bm bitsPerPixel])>>3;
  363.     if (numFrames*flen>20000000) [self setNoOriginals];
  364.     if (!movieFrame) {
  365.     numFrames=1;
  366.     NX_MALLOC(movieFrame, movieFrameStruct, numFrames);
  367.     } else {
  368.     numFrames++;
  369.     NX_REALLOC(movieFrame, movieFrameStruct, numFrames);
  370.     }
  371.     currentFrameIndex=numFrames-1;
  372.     if (!noOriginals) movieFrame[currentFrameIndex].original=bm;
  373.     else movieFrame[currentFrameIndex].original=nil;
  374.  //    printf("Frame # %d, Size: %8.3f %8.3f\n", currentFrameIndex, sz.width, sz.height);
  375.     if (!fromStream || !currentFrameIndex) {
  376.     movieFrame[currentFrameIndex].image=[[NXImage alloc] initSize:&sz];
  377.     [movieFrame[currentFrameIndex].image setUnique:YES]; /* make caches disjoint */
  378.     [movieFrame[currentFrameIndex].image setBackgroundColor:NX_COLORBLACK];
  379.     } else {
  380.     movieFrame[currentFrameIndex].image=movieFrame[currentFrameIndex-1].image;
  381.     }
  382.     [self getBounds:&r];
  383.     if ([movieFrame[currentFrameIndex].image lockFocus]) {
  384.     [bm drawIn:&r];
  385.     [movieFrame[currentFrameIndex].image unlockFocus];
  386.     } else error("Could not lock focus on image");
  387.     if (noOriginals) [bm free];
  388.     if (numFrames==1) [self makeWindow];
  389.     else [self display];
  390.     NXPing();
  391.     return self;
  392.  }
  393.  
  394. NXWindowDepth deps[] = {
  395.   NX_DefaultDepth, NX_TwoBitGrayDepth,
  396.   NX_EightBitGrayDepth, NX_TwelveBitRGBDepth,
  397.   NX_TwentyFourBitRGBDepth /*,NX_PurinaCatChow__ChowChowChowDepth*/
  398.   };
  399.  
  400. // set up Frame data structures and find max frame size
  401. - allocateFrames:(List *)frames
  402.  {
  403.   int i;
  404.   numFrames=[frames count];
  405.   NX_MALLOC(movieFrame,movieFrameStruct,numFrames);
  406.   for(i=0;i<numFrames;i++) {
  407.     NXImage *nxi;
  408.     NXBitmapImageRep *bm=[frames objectAt:i];
  409.     NXSize sz;
  410.     [bm getSize:&sz];
  411.     movieFrame[i].original=bm;
  412.     nxi=movieFrame[i].image=[[NXImage alloc] initSize:&sz];
  413.     [nxi setUnique:YES]; /* make caches disjoint */
  414.     [nxi setBackgroundColor:NX_COLORBLACK];
  415.     /* keep track of largest frame */
  416.     if (sz.width > maxSize.width) maxSize.width=sz.width;
  417.     if (sz.height > maxSize.height) maxSize.height=sz.height;
  418.     };
  419.   return self;
  420.  }
  421.  
  422. /*****************************************************************
  423.  *****************************************************************/
  424. - superviewSizeChanged:(NXSize *)old
  425.  {
  426.     [anim stopEntry];
  427.     [super superviewSizeChanged:old];
  428.     if (noOriginals) {
  429.     if (!fromStream)
  430.         NXRunAlertPanel("Resize","Can't resize, no originals.",NULL,NULL,NULL);
  431.     }
  432.     else if (state!=LOADING) {
  433.     [self recache];
  434.     [self renderFrames];
  435.     }
  436.     if (movieFrame) [[self window] display];
  437.     NXPing();
  438.     [anim resetRealTime];
  439.     [anim startEntry];
  440.     return self;
  441.  }
  442.  
  443. - renderFrames
  444.  {
  445.     int cfi;
  446.     NXRect r;
  447.  
  448.     [self getBounds:&r];
  449.     cfi = currentFrameIndex;
  450.     state = LOADING;
  451.     for(currentFrameIndex=0; currentFrameIndex<numFrames; currentFrameIndex++) {
  452.     if ([movieFrame[currentFrameIndex].image lockFocus]) {
  453.         [movieFrame[currentFrameIndex].original drawIn:&r];
  454.         [movieFrame[currentFrameIndex].image unlockFocus];
  455.     } else {
  456.         fprintf(stderr,"Barf: couldn't lockFocus on image %d\n",
  457.             (int)movieFrame[currentFrameIndex].image);
  458.         abort();
  459.     }
  460.     [self display];
  461.     NXPing();
  462.     }
  463.     state = STOPPED;
  464.     currentFrameIndex = cfi;
  465.     return self;
  466.  }
  467.  
  468. - recache
  469.  // assume depth & size both changed 
  470.  //
  471.  // Appkit bug?  Can one render down from 24 bit color to 2 bit gray?
  472.  //
  473.  {
  474.     NXRect r;
  475.     int i;
  476.     [self getBounds:&r];
  477.     [self freeCaches];
  478.     for(i=0; i<numFrames; i++) {
  479.     movieFrame[i].image=[[NXImage alloc] initSize:&r.size];
  480.     [movieFrame[i].image useCacheWithDepth:deps[(int)dmode]];
  481.     };
  482.     return self;
  483.  }
  484.  
  485. - save:sender
  486.  {
  487.   const char *type = "anim"; // will only save in .anim format.
  488.   SavePanel *sp = [SavePanel new];
  489.   [sp setDelegate:self];
  490.   [sp setRequiredFileType:type];
  491.   if ([sp runModal]) { // OK was hit
  492.     int i;
  493.     char cwd[MAXPATHLEN];
  494.     /* if directory exists, rename it with a wiggle in back. */
  495.     if (access([sp filename],F_OK) == 0) {
  496.       /* I could do this with a couple of calls to system(), but noooo,
  497.        * I had to do it the had way. yeccch. */
  498.       char *buf=malloc(strlen([sp filename]+2));
  499.       sprintf(buf,"%s~",[sp filename]);
  500.       if (!getwd(cwd)) {
  501.         NXRunAlertPanel("FATAL","Couldn't get current directory.",NULL,NULL,NULL);
  502.         abort();
  503.         };
  504.       if (rename([sp filename],buf) == -1) {
  505.         // sledgehammer time.
  506.         struct direct *de;
  507.         DIR *dp;
  508.         chdir([sp filename]);
  509.         dp=opendir(".");
  510.         while(de=readdir(dp)) unlink(de->d_name);
  511.         closedir(dp);
  512.         chdir(cwd);
  513.         unlink([sp filename]);
  514.         };
  515.       };
  516.     mkdir([sp filename],0755);
  517.     chdir([sp filename]);
  518.     for(i=0;i<numFrames;i++) {
  519.       char buf3[MAXPATHLEN];
  520.       char buf2[MAXPATHLEN];
  521.       char *ptr;
  522.       int fd;
  523.       NXStream *s;
  524.       strcpy(buf3,[sp filename]);
  525.       ptr=rindex(buf3,'/')+1;
  526.       *(rindex(ptr,'.'))='\0';
  527.       sprintf(buf2,"./%s.%d.tiff",ptr,i+1);
  528.       fd=open(buf2,O_WRONLY|O_CREAT,0644);
  529.       s=NXOpenFile(fd,NX_WRITEONLY);
  530.       [movieFrame[i].image writeTIFF:s];
  531.       NXClose(s);
  532.       close(fd);
  533.       };
  534.     chdir(cwd);
  535.     };
  536.   return self;
  537.  }
  538.  
  539. - (BOOL) panelValidateFilenames:sender
  540.  {
  541.   if (!strcmp([sender filename],moviePath)) {
  542.     NXRunAlertPanel("Save","Cannot overwrite original movie",NULL,NULL,NULL);
  543.     return NO;
  544.     };
  545.   return YES;
  546.  }
  547.  
  548. - freeCaches
  549.  {
  550.   int i;
  551.   for(i=0;i<numFrames;i++) [movieFrame[i].image free];
  552.   return self;
  553.  }
  554.  
  555. - freeOriginals
  556.  {
  557.     int i;
  558.     for(i=0;i<numFrames;i++) {
  559.     [movieFrame[i].original free];
  560.     movieFrame[i].original = nil;
  561.     }
  562.     return self;
  563.  }
  564.  
  565. - free
  566.  {
  567.   [self freeCaches];
  568.   [self freeOriginals];
  569.   [self freeGState];
  570.   [anim free];
  571.   anim=nil;
  572.   return [super free];
  573.  }
  574.  
  575. - copy:sender
  576.  {
  577.     char *buffer;
  578.     NXStream *stream;
  579.     int length, maxLength;
  580.     Pasteboard *pasteboard = [Pasteboard new];
  581.     runState s=state;
  582.  
  583.     [anim stopEntry];
  584.     if (s!=STOPPED) [self stop:self];
  585.     [pasteboard declareTypes:&NXPostScriptPboardType num:1 owner:self];
  586.     stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  587.     [self copyPSCodeInside:&bounds to:stream];
  588.     NXFlush(stream);
  589.     NXGetMemoryBuffer(stream, &buffer, &length, &maxLength);
  590.     [pasteboard writeType:NXPostScriptPboard data:buffer length:length];
  591.     NXCloseMemory(stream, NX_FREEBUFFER);
  592.     switch(s) {
  593.     case STOPPED:
  594.     case LOADING: break;
  595.     case FORWARD: 
  596.     case REVERSE: [anim startEntry]; break;
  597.     };
  598.     return self;
  599.  }
  600.  
  601. - tick:sender
  602.  {
  603.     int next, end=(state == FORWARD) ? numFrames-1 : 0;
  604.  
  605.     switch(mode) {
  606.     case ONCE:
  607.         if (currentFrameIndex == end) {
  608.         [self stop:self];
  609.         return self;
  610.         } else currentFrameIndex += (int)state;
  611.         break;
  612.     case LOOP:
  613.         next = currentFrameIndex + (int)state;
  614.         if (((state == FORWARD)&&(next>end)) ||
  615.         ((state == REVERSE)&&(next<end))) {
  616.         currentFrameIndex = (state < 0) ? numFrames-1 : 0;
  617.         } else currentFrameIndex = next;
  618.         break;
  619.     case BOUNCE:
  620.         next = currentFrameIndex + (int)state;
  621.         if (((state == FORWARD)&&(next>end)) ||
  622.         ((state == REVERSE)&&(next<end))) {
  623.         if (state == FORWARD) [self selectStateButton:REV];
  624.         if (state == REVERSE) [self selectStateButton:FWD];
  625.         state *= -1;
  626.         currentFrameIndex += (int)state;
  627.         } else currentFrameIndex = next;
  628.         break;
  629.     };
  630.     frameCount++;
  631.     [self display];
  632.     return self;
  633.  }
  634.  
  635. /*****************************************************************
  636.  *****************************************************************/
  637.  
  638. - fwd:sender
  639.  {
  640.  
  641.     if (numFrames < 2) return self;
  642.     if (state != STOPPED) [self stop:self];
  643.     state = FORWARD;
  644.     [self move:sender];
  645.     return self;
  646.  }
  647.  
  648. - rev:sender
  649.  {
  650.   if (numFrames < 2) return self;
  651.   if (state != STOPPED) [self stop:self];
  652.   state = REVERSE;
  653.   [self move:sender];
  654.   return self;
  655.  }
  656.  
  657. - move:sender
  658.  {
  659.     double period = 1.0/frameRate;
  660.     if (numFrames < 2) { /* duh */
  661.     [self selectStateButton:STOP];
  662.     return self;
  663.     };
  664.     anim = [[Animator alloc] initChronon:period adaptation:0.05 /*?*/
  665.                 target:self action:@selector(tick:)
  666.                 autoStart:YES eventMask:0];
  667.     if (state == FORWARD) [self selectStateButton:FWD];
  668.     if (state == REVERSE) [self selectStateButton:REV];
  669.     [fNumText setStringValue:""];
  670.     [fNumSlider setEnabled:NO];
  671.     oldt=[anim getSyncTime];
  672.     frameCount = 0;
  673.     return self;
  674.  }
  675.  
  676. - stop:sender
  677.  {
  678.     if (numFrames < 2) return self;
  679.     switch(state) {
  680.     case FORWARD:
  681.     case REVERSE: [anim free]; anim=nil;
  682.     case STOPPED:
  683.     case LOADING: break;
  684.     }
  685.     state = STOPPED;
  686.     [self selectStateButton:STOP];
  687.     [fNumText setIntValue:currentFrameIndex];
  688.     [fNumSlider setEnabled:YES];
  689.     [fNumSlider setIntValue:currentFrameIndex];
  690.     return self;
  691.  }
  692.  
  693. - fwdStep:sender
  694.  {
  695.   [self step:(int) FORWARD];
  696.   return self;
  697.  }
  698.  
  699. - revStep:sender
  700.  {
  701.   [self step:(int) REVERSE];
  702.   return self;
  703.  }
  704.  
  705. - step:(int) direction
  706.  {
  707.   if (numFrames < 2) return self;
  708.   if (state != STOPPED) [self stop:self];
  709.   if ((currentFrameIndex = (currentFrameIndex + direction) % numFrames) < 0)
  710.     currentFrameIndex = numFrames + currentFrameIndex;
  711.   [self selectStateButton:STOP];
  712.   [fNumText setIntValue:currentFrameIndex];
  713.   [fNumSlider setEnabled:YES];
  714.   [fNumSlider setIntValue:currentFrameIndex];
  715.   return [self display];
  716.  }
  717.  
  718. - reSize:(NXSize *)s
  719. {
  720.     if (noOriginals)
  721.     NXRunAlertPanel("Resize","Resize disabled for lack of memory.",NULL,NULL,NULL);
  722.     else [[self window] sizeWindow:s->width :s->height];
  723.     return self;
  724. }
  725.  
  726. - expand2x:sender
  727.  {
  728.   NXRect r;
  729.   [self getBounds:&r];
  730.   r.size.width *= 2.0;
  731.   r.size.height *= 2.0;
  732.   [self reSize:&r.size];
  733.   return self;
  734.  }
  735.  
  736. - reduce50pct:sender
  737.  {
  738.   NXRect r;
  739.   [self getBounds:&r];
  740.   r.size.width *= 0.5;
  741.   r.size.height *= 0.5;
  742.   [self reSize:&r.size];
  743.   return self;
  744.  }
  745.  
  746. - restore:sender
  747.  {
  748.   [self reSize:&maxSize];
  749.   return self;
  750.  }
  751.  
  752.  
  753. - modeButtonsChanged:sender
  754.  {
  755.   mode = (runMode)[sender selectedRow];
  756.   return self;
  757.  }
  758.  
  759. - fNumSliderChanged:sender
  760.  {
  761.   if (currentFrameIndex == [sender intValue]) return self;
  762.   currentFrameIndex=[sender intValue];
  763.   [self stop:self];
  764.   [self display];
  765.   return self;
  766.  }
  767.  
  768. - fpsSliderChanged:sender
  769.  {
  770.     frameRate = [sender floatValue];
  771.     [fpsText setFloatValue:frameRate];
  772.     switch(state) {
  773.     case FORWARD: 
  774.     case REVERSE: {
  775.         double period = 1.0/frameRate;
  776.         [anim free];
  777.         anim = [[Animator alloc] initChronon:period adaptation:0.05
  778.                     target:self action:@selector(tick:)
  779.                     autoStart:YES eventMask:0];
  780.         break;
  781.         };
  782.     case STOPPED:
  783.     case LOADING: break;
  784.     };
  785.     return self;
  786.  }
  787.  
  788. - pingButtonChanged:sender
  789.  {
  790.   switch([sender selectedRow]) {
  791.     case 0: pingDuringDisplay=NO; break;
  792.     case 1: pingDuringDisplay=YES; break;
  793.     };
  794.   return self;
  795.  }
  796.  
  797. - selectStateButton:(runState)b
  798.  {
  799.   [stateButtons selectCellAt:0:((int)b)];
  800.   return self;
  801.  }
  802.  
  803. - depthButtonsChanged:sender
  804.  {
  805.     if (noOriginals) {
  806.     NXRunAlertPanel("Depth","Can't change depth, no originals.",NULL,NULL,NULL);
  807.     return self;
  808.     }
  809.     dmode=(depthMode)[sender selectedRow];
  810.     [anim stopEntry];
  811.     [self recache];
  812.     [self renderFrames];
  813.     [self display];
  814.     [anim resetRealTime];
  815.     [anim startEntry];
  816.     return self;
  817.  }
  818.  
  819. - updateCheckBoxChanged:sender
  820.  {
  821.   updateControls = !updateControls;
  822.   return self;
  823.  }
  824.  
  825. - frameCheckBoxChanged:sender
  826.  {
  827.     showFrameNumber = !showFrameNumber;
  828.     return self;
  829.  }
  830.  
  831.  
  832. // Window's delegate methods
  833.  
  834. - windowWillClose:sender
  835.  {
  836.   [panel close];
  837.   [self free];
  838.   return self;
  839.  }
  840.  
  841. -windowDidMiniaturize:sender
  842.  {
  843.   [panel orderOut:sender];
  844.   [anim stopEntry];
  845.   return self;
  846.  }
  847.  
  848. -windowDidDeminiaturize:sender
  849.  {
  850.   [panel orderFront:sender];
  851.   [anim resetRealTime];
  852.   [anim startEntry];
  853.   return self;
  854.  }
  855. @end
  856.